/******************************************************************************
 *  _____     _____
 *  \  __\    \  __\
 *   \ \_\\    \ \_\\
 *    \  __\SS  \  __\roject
 *     \_\       \_\
 *
 * Mateusz Maciąg, Rafał Urbański
 * Komputerowe Systemy Sterowania
 *****************************************************************************/

#ifndef DISCRETEOBJECTPARAMS_H
#define DISCRETEOBJECTPARAMS_H

#include <vector>
#include <iosfwd>
#include <queue>

#include "Polynomial.h"
#include "SimInterface/IConfigurable.h"

#include "WrmnkARXWrapper.h"

#include <boost/smart_ptr.hpp>

namespace RafMat
{
namespace SimEngine
{

// Deklaracja zapowiadająca
class DiscreteObject;
class DiscreteObjectParams;

/** Alias na zręczny wskaźnik na obiekt typu DiscreteObjectParams */
typedef boost::shared_ptr<DiscreteObjectParams> DiscreteObjectParamsSPtr;


/**
    Klasa przechowująca parametry modelu ARX.
    Model ARX może przechowywać kilka instancji tej klasy i przełączać je
    w odpowiedniej chwili (objekty niestacjonarne).

    Pojedynczy obiekt tej klasy może opisywać jedynie model stacjonarny ARX.
    Tzn. z ściśle określoną funkcją przejścia.

    Funkcję przejścia określamy poprzez dwa wielomiany: licznika B(z^-1) i mianownika A(z^-1),
    oraz opóźnienie (parametr z^-k). Z założenia wyraz wolny wielomianu A wynosi 1,
    tak aby łatwo było wrócić do przestrzeni czasowej (wzór y(i) = ...).
    Nie mniej klasa umożliwia pobranie dowolnego wielomianu A, który to wielomian
    zostaje później znormalizowany (podzielony przez a0). Dzielenie wpływa oczywiście
    na postać całego równania y(i) = ... a więc także na wariancję szumu losowego.
    Dzięki temu mamy wpływ na szum obiektu. Należy jednak pamiętać, że podanie obiektów
    o transmitancji [1]/[1] da inny efekt niż [100]/[100] - w drugim przypadku,
    szum będzie 100x mniejszy.

    Obiekty klasy przechowują również moment przełączenia zestawu parametrów w
    modelach niestacjonarnych. Ustawienie wartości <= 0, czyni zestaw parametrów
    aktualnym przez cały czas (modele stacjonarne) o ile w tym samym obiekcie
    nie znajdzie się inny zestaw z dodatnim czasem przełączenia.
    W praktyce możliwość podawania wartości < -1 została zablokowana dla GUI,
    dostępna jest jedynie dla obiektu symulatora.

    Klasa dziedziczy po IConfigurable, więc może być modyfikowana przez łańcuchy znaków.

    Dostępne komendy:
    - apoly - składnia: [a0 a1 ... adA];
    - bpoly - j.w.;
    - delay - liczba całkowita nieujemna;
    - switch_time - czas przełączenia - liczba całkowita.
*/
class DiscreteObjectParams : public SimInterface::IConfigurable
{
public:

    //
    // Aliasy dla skomplikowanych typów:
    //
    /** Alias na wektor liczb zmiennoprzecinkowych */
    typedef std::vector<double> PolyType;

    /** Alias na iterator dla wektora PolyType */
    typedef std::vector<double>::iterator PolyIterator;

    /** Alias na stały iterator dla wektora PolyType */
    typedef std::vector<double>::const_iterator PolyConstIterator;

    /**
        Tworzy zbiór parametrów obiektu ARX i przypisuje go obiektowi dyskretnemu ARX.
        Parametry podawane są poprzez wielomiany (Polynomial) oraz wartość opóźnienia
        (liczba całkowita) oraz moment przełączenia.
        Domyśle parametry odpowiadają obiektowi typu 'bypass'.

        \param [in] owner Właściciel tego zestawu parametrów.
        \param [in] a Mianownik transmitancji.
        \param [in] b Licznik transmitancji.
        \param [in] delay Opóźnienie obiektu.
        \param [in] switchTime Czas przełączenia.
    */
    explicit DiscreteObjectParams(const DiscreteObject &owner,
                                  Polynomial a = 1,
                                  Polynomial b = 1,
                                  Polynomial c = 1,
                                  Polynomial ag = 1,
                                  double d = 0,
                                  unsigned int delay = 0, int switchTime = -1);

    /**
        Tworzy zbiór parametrów obiektu ARX i przypisuje go obiektowi dyskretnemu ARX.
        Parametry podawane są poprzez wielomiany (jako wektory STL) oraz wartość opóźnienia
        (liczba całkowita) oraz moment przełączenia. Niezdefiniowano domyślnych parametrów
        dla wielomianów.

        \param [in] owner Właściciel tego zestawu parametrów.
        \param [in] va Mianownik transmitancji (wektor).
        \param [in] vb Licznik transmitancji (wektor).
        \param [in] delay Opóźnienie obiektu.
        \param [in] switchTime Czas przełączenia.
    */
    DiscreteObjectParams(const DiscreteObject &owner,
                         PolyType va,
                         PolyType vb,
                         PolyType vc,
                         PolyType vag,
                         unsigned int d = 0,
                         unsigned int delay = 0, int switchTime = -1);

    /**
        Zwraca opóźnienie obiektu w próbkach.
        \return Opóźnienie obiektu (liczba stałoprzecinkowa).
    */
    unsigned int getDelay() const { return m_delay; }

    /**
        Zwraca czas wejścia parametrów do bieżących.
        \return Czas przełączenia (liczba stałoprzecinkowa).
    */
    int getSwitchTime() const { return m_switchTime; }

    /**
        Zwraca wzmocnienie(wariancję) szumu losowego obiektu.
        Wzmocnienie to zależy od stopnia "skrócenia" ułamka transmitancji.
        \return Wartość szumu.
    */
    double getNoiseGain() const { return m_noiseGain; }

    double getDeltaGain() const { return m_deltaGain; }

    /**
        Zwraca stopień (rząd) mianownika transmitancji.
    */
    int getADegree() const { return m_a.degree(); }

    /**
        Zwraca stopień (rząd) licznika transmitancji.
    */
    int getBDegree() const { return m_b.degree(); }

    /**
        Zwraca stopień (rząd) licznika transmitancji prz e(i).
    */
    int getCDegree() const { return m_c.degree(); }

    /**
        Zwraca stopień (rząd) mianownika transmitancji prz delta(i).
    */
    int getAgDegree() const { return m_ag.degree(); }

    /**
        Zwraca d obiektu .
        \return D obiektu (liczba stałoprzecinkowa - po ch*j !?).
    */
    double getD() const { return m_d; }

    /** Ustawia funkcję przejścia. */
    void setTransferFcn(const Polynomial& Ag, double d, const Polynomial& C, const Polynomial& B, const Polynomial& A, unsigned delay);

    /** Ustawia funckję przejścia na podstawie obiektu do identyfikacji. */
    void setTransferFcn(WrmnkARXWrapperSPtr fromRmnk);

    //
    // Implementacja interfejsu IConfigurable
    //
    /**
        Ustawia wartość danego parametru.
        Funkcja implementuje interfejs IConfigurable.

        Dostępne komendy param:
        - \c apoly - Składnia wektora: [a0 a1 ... adA];
        - \c bpoly - j.w.;
        - \c delay - liczba całkowita nieujemna;
        - \c switch_time - czas przełączenia - liczba całkowita.
    */
    bool setParameter(const std::string& param, const std::string& value) throw(std::logic_error);

    /**
        Zwraca wartość danego parametru.
        Dostępne parametry takie jak w setParameter().
    */
    std::string getParameter(const std::string& param) const throw(std::logic_error);

    /**
        Wyprowadza listę parametrów do strumienia.
    */
    SimFile::ConfigWriter& dumpParameters(SimFile::ConfigWriter& o) const;

    //
    // Iteratory
    //

    /** Zwraca iterator wskazujący na a0. */
    PolyIterator abegin() { return m_a.begin(); }

    /** Zwraca iterator wskazujący współczynnik na a_dA. */
    PolyIterator aend() { return m_a.end(); }

    /** Zwraca iterator wskazujący współczynnik na a0 (wersja const). */
    PolyConstIterator abegin() const { return m_a.begin(); }

    /** Zwraca iterator wskazujący współczynnik na a_dA (wersja const). */
    PolyConstIterator aend() const { return m_a.end(); }

    /** Zwraca iterator wskazujący współczynnik na b0. */
    PolyIterator bbegin() { return m_b.begin(); }

    /** Zwraca iterator wskazujący współczynnik na b_dB. */
    PolyIterator bend() { return m_b.end(); }

    /** Zwraca iterator wskazujący współczynnik na b0 (wersja const). */
    PolyConstIterator bbegin() const { return m_b.begin(); }

    /** Zwraca iterator wskazujący na współczynnik b_dB (wersja const). */
    PolyConstIterator bend() const { return m_b.end(); }

    /** Zwraca iterator wskazujący współczynnik na c0. */
    PolyIterator cbegin() { return m_c.begin(); }

    /** Zwraca iterator wskazujący współczynnik na c_dC. */
    PolyIterator cend() { return m_c.end(); }

    /** Zwraca iterator wskazujący współczynnik na c0 (wersja const). */
    PolyConstIterator cbegin() const { return m_c.begin(); }

    /** Zwraca iterator wskazujący na współczynnik b_dB (wersja const). */
    PolyConstIterator cend() const { return m_c.end(); }

    /** Zwraca iterator wskazujący współczynnik na ag0. */
    PolyIterator agbegin() { return m_ag.begin(); }

    /** Zwraca iterator wskazujący współczynnik na ag_dAg. */
    PolyIterator agend() { return m_ag.end(); }

    /** Zwraca iterator wskazujący współczynnik na ag0 (wersja const). */
    PolyConstIterator agbegin() const { return m_ag.begin(); }

    /** Zwraca iterator wskazujący na współczynnik ag_dAg (wersja const). */
    PolyConstIterator agend() const { return m_ag.end(); }

private:

    // Stałe łańcuchowe na użytek interfejsu IConfigurable.
    static const std::string CONF_APOLY;
    static const std::string CONF_BPOLY;
    static const std::string CONF_CPOLY;
    static const std::string CONF_AGPOLY;
    static const std::string CONF_D;
    static const std::string CONF_DELAY;
    static const std::string CONF_SWITCH_TIME;

    /**
        Rekalkuluje parametry obiektu do postaci znormalizowanej.
        Funkcja wymierna transmitancji jest skracana tak, aby a0 = 1.
        Ma to wpływ na wariancję szumu.
    */
    void reset();

    // TODO: zmienić na zręczny wskaźnik.
    const DiscreteObject &m_owner;    ///< Referencja do obiektu, który jest właścicielem tego zestawu.

    Polynomial m_a;             ///< Mianownik transmitancji.
    Polynomial m_b;             ///< Licznik transmitancji.
    Polynomial m_c;             ///< Licznik transmitancji przy e(i).
    Polynomial m_ag;             ///< Licznik transmitancji przy e(i).
    double m_d;                 ///< d
    unsigned int m_delay;       ///< Opóźnienie transportowe.
    int m_switchTime;           ///< Moment przełączenia parametrów.
    double m_noiseGain;         ///< Wzmocnienie szumu.
    double m_deltaGain;         ///< Wzmocnienie szumu.
};

}   // SimEngine namespace
}   // RafMat namespace

#endif // DISCRETEOBJECTPARAMS_H
